#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import json
import time
from functools import wraps

from dws.commons.common import get_cur_host_all_ip, log
from dws.commons.const import DwsBackupLabel
from dws.commons.dws_exception import ErrCodeException
from dws.commons.job_info import JobInfo
from dws.commons.progress_notify import get_progress_path, write_file

from common.common import output_result_file
from common.common_models import ActionResult, LogDetail, SubJobDetails
from common.const import DBLogLevel, ExecuteResultEnum, SubJobStatusEnum


def log_start():
    """
    功能描述： 函数开始-结束添加日志打印
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            log.info(f"Enter {f.__name__}")
            ret = f(*args, **kwargs)
            log.info(f"End with [{ret}], {f.__name__}")
            return ret

        return inner

    return decorator


def out_result():
    """
    功能描述： 函数结果输出
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            if len(args) < 2:
                raise Exception("Param err")
            job_info = args[1]
            ret = False
            body_err = None
            body_err_params = None
            try:
                ret = f(*args, **kwargs)
            except ErrCodeException as err:
                body_err = err.get_log_detail().log_detail
                body_err_params = err.get_log_detail().log_detail_param
                log.error(f"{f.__name__} raise ErrCodeException, " \
                          f"body_err: {body_err}, body_err_params: {body_err_params}, {job_info.log_format()}.")
                raise err
            except Exception as err:
                log.error(f"{f.__name__} run err: {err}, {job_info.log_format()}.")
                raise err
            finally:
                ret_code = ExecuteResultEnum.SUCCESS if ret else ExecuteResultEnum.INTERNAL_ERROR
                output_result_file(
                    job_info.pid,
                    ActionResult(code=ret_code, bodyErr=body_err, bodyErrParams=body_err_params).dict(by_alias=True))
                log.info(f"{f.__name__} write result suc, {job_info.log_format()}.")

            return ret

        return inner

    return decorator


def get_register_ip(cluster_agents, host_agents):
    """
    获取当前节点注册ip
    :param cluster_agents:
    :param host_agents:
    :return:
    """
    cur_host_ips = set(get_cur_host_all_ip())
    all_remote_ips = set(cluster_agents + host_agents)
    log.debug(f"Current host ips: {cur_host_ips}, all remote ips: {all_remote_ips}.")
    result_set = cur_host_ips & all_remote_ips
    if not result_set or len(result_set) != 1:
        log.error(f"Get register ip failed, result_set count: {len(result_set)}.")
        return ""
    return result_set.pop()


def fill_log_info_param(log_details: list, job_info: JobInfo):
    cur_host_ip = get_register_ip(job_info.cluster_agents, job_info.host_agents)
    for item in log_details:
        if item["logInfo"] == DwsBackupLabel.WHITE_SUB_JOB_SUC_LABEL:
            item["logInfoParam"] = [job_info.sub_job_id]
        elif item["logInfo"] == DwsBackupLabel.WHITE_SUB_JOB_FAILED_LABEL:
            item["logInfoParam"] = [cur_host_ip, job_info.sub_job_id]
        elif item["logInfo"] == DwsBackupLabel.BACKUP_SUBJOB_SUC_LABEL:
            item["logInfoParam"] = [cur_host_ip, job_info.sub_job_id]
        elif item["logInfo"] == DwsBackupLabel.BACKUP_SUBJOB_FAILED_LABEL:
            item["logInfoParam"] = [cur_host_ip, job_info.sub_job_id]


def progress_notify(suc_lab=None, fail_lab=None):
    """
    功能描述： 进度结果输出
    suc_lab: 成功任务信息提示label
    fail_lab： 失败任务提示信息label
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            job_info = args[1]
            ret = False
            log_detail = None
            try:
                ret = f(*args, **kwargs)
            except ErrCodeException as err:
                log.error(f"{f.__name__} raise exception: {err.get_log_detail()}, {job_info.log_format()}.")
                log_detail = [err.get_log_detail().dict(by_alias=True)]
                raise err
            except Exception as err:
                log.error(f"{f.__name__} run err: {err}, {job_info.log_format()}.")
                raise err
            finally:
                if not log_detail and fail_lab and not ret:
                    log_detail = [
                        LogDetail(logInfo=fail_lab, logTimestamp=int(time.time()),
                                  logLevel=DBLogLevel.ERROR).dict(by_alias=True)
                    ]
                if not log_detail and suc_lab and ret:
                    log_detail = [
                        LogDetail(logInfo=suc_lab, logTimestamp=int(time.time()),
                                  logLevel=DBLogLevel.INFO).dict(by_alias=True)
                    ]
                if log_detail:
                    # 根据label名称来填充不同的log_info_param
                    fill_log_info_param(log_detail, job_info)
                write_file(
                    get_progress_path(job_info.copy_id, job_info.sub_job_id, job_info.cache_path),
                    json.dumps(
                        SubJobDetails(
                            taskId=job_info.job_id,
                            subTaskId=job_info.sub_job_id,
                            taskStatus=SubJobStatusEnum.COMPLETED if ret else SubJobStatusEnum.FAILED,
                            progress=100 if ret else 0,
                            logDetail=log_detail).dict(by_alias=True)))
                log.info(f"{f.__name__} write progress suc, {job_info.log_format()}.")

            return ret

        return inner

    return decorator


def retry(exception_to_check=BaseException, tries=3, delay=5):
    """ 重试装饰器，用于在特定异常下进行函数级重试 """

    def deco_retry(func):
        @wraps(func)
        def f_retry(*args, **kwargs):
            cnt = 1
            while cnt <= tries:
                try:
                    return func(*args, **kwargs)
                except exception_to_check as exception:
                    log.warn(f'Caught exception: {str(exception)} then after {delay} seconds will do the {cnt}th retry')
                    time.sleep(delay)
                    cnt += 1
            return func(*args, **kwargs)

        return f_retry

    return deco_retry


def thread_lock_func(lock):
    """
    功能描述： 线程锁
    """

    def decorator(func):
        @wraps(func)
        def inner(*args, **kwargs):
            lock.acquire()
            try:
                ret = func(*args, **kwargs)
            except Exception as err:
                lock.release()
                log.error(f"Happen exception f{func.__name__} exception {err}")
                raise err
            lock.release()
            return ret

        return inner

    return decorator
